昨天在實作「顯示通知」時,除了設定body這個option之外,其實還有很多其他選項可以設定 (不過這裡要先說這些額外的設定不一定每個browser都有支援QQ):
function displayConfirmNotification() {
if('serviceWorker' in navigator) {
var options = {
body: '您已成功訂閱我們的推播服務!',
icon: '/src/images/icons/app-icon-96x96.png',
image: '/src/images/sf-boat.jpg',
dir: 'ltr',
lang: 'zh-TW', // BCP 47
vibrate: [100, 50, 200],
badge: '/src/images/icons/app-icon-96x96.png',
tag: 'confirm-notification',
renotify: true,
actions: [
{ action: 'confirm', title: '收到', icon: '/src/images/icons/app-icon-96x96.png' },
{ action: 'cancel', title: '取消', icon: '/src/images/icons/app-icon-96x96.png' }
]
}
navigator.serviceWorker.ready.then(function(swreg) {
swreg.showNotification('成功訂閱!! (from Service Worker)', options);
});
}
}
我一個個說明一下每個選項代表的意義:
來看一下顯示通知的結果吧:
針對顯示通知的actions,我們可以在servie worker中針對用戶的動作進行相對的處理,也就是說當用戶收到通知按下收到鈕後我們可以再做進一步的邏輯處理(像是跳回我的PWA網站之類的):
來看一下在sw.js中要怎寫吧:
self.addEventListener('notificationclick', function(event) {
var notification = event.notification;
var action = event.action;
console.log(notification);
if(action === 'confirm') {
console.log('Confirm was chosen');
notification.close();
} else {
console.log(action);
notification.close();
}
});
在service worker中開始監聽notificationclick event,當用戶按下不同的動作鈕後,就會進行不同的處理。根據我剛剛設定的actions(有confirm和cacnel兩種動作),目前我只印出log來表示service worker確實有監聽到用戶對推播通知進行的動作。
再來,如果用戶都沒有按下動作鈕,只是把這則通知關掉呢?service worker也可以監聽到這類型的事件,不過要監聽的事件跟剛剛的不太一樣:
self.addEventListener('notificationclose', function(event) {
console.log('Notification was closed', event);
})
回顧一下一開始的推播流程圖,我已經完成Display Notification,接下來要實作push messages的部分。
可以發現push分成很多個步驟,第一步當然是必須取得用戶訂閱(subscription)的資訊(不管是新建一個或是從server那邊取得)。
在我的PWA project中,使用情境是當用戶按下訂閱鈕後,我就會為用戶建立一個訂閱,來看要怎麼在app.js中實作「創建新的訂閱」吧:
首先在原本的askForNotificationPermission() funciton中的displayConfirmNotification()註解掉,並改為下列的函式:(要這樣做因為我希望用戶允許通知後,先為他註冊一個訂閱,最後才顯示通知)
function askForNotificationPermission() {
Notification.requestPermission(function(result) {
console.log('User Choice', result);
if(result !== 'granted') {
console.log('No notification permission granted!');
} else {
configurePushSub();
// displayConfirmNotification();
}
});
}
function configurePushSub() {
if(!('serviceWorker' in navigator)) {
return;
}
var reg;
navigator.serviceWorker.ready.then(function(swreg) {
reg = swreg;
return swreg.pushManager.getSubscription();
}).then(function(sub) {
if(sub === null) {
// Create a new subscription
reg.pushManager.subscribe({
userVisibleOnly: true
});
} else {
// We have a subscription
}
})
}
跟前面流程圖相同我們必須透過service worker來創建,所以要先取得已經註冊完成的service worker(這裡把它命名為swreg),接著使用pushManager.getSubscription()這個method來取得訂閱資訊,它會回傳一個promise。我們可以根據回傳的訂閱資訊來進行相對應的處理。如果回傳是null的話,代表用戶尚未創建訂閱,透過pushManager.subscribe() method幫他創建一個。
之前我有提到向瀏覽器供應商(Google、Mozilla)創建一個訂閱時,它會回傳一個API endpoint url,之後我的server會將要推播的貼文資訊傳送到這個API。
不過為了避免任何人知道這個url後,就可以向我的用戶推播垃圾訊息,這裡我們需要一個防護的機制,也就是subscribe() method中輸入的參數。
我將userVisibleOnly這個property設為true,代表從我的server發出推播訊息時,只有該用戶會看到。不過這還沒解決剛剛的問題RRRR
較好的解決方法我的server在push message時,先使用「簽章」加密再傳送到瀏覽器供應商的API Endpoint,如此就可以證明傳送到push server的確是我的server而不是其他Hacker。
這裡當然有現成的library讓我們來使用囉,叫作VAPID。不過這是明天的內容了XDD
Day23 結束!!